#include <stdint.h>
#include <sys/stat.h>
#include <openssl/md5.h>
#include <sys/time.h>
#define MY_PACKET_SIZE 1048
#define MAX_WINDOW_SIZE 10
#define MAX_RESENDS 20
#define TIMEOUT 100
#define INIT 0x80
#define RECV 0x40
#define SEND 0x20
#define DIR_REQ 0x10
#define NACK 0x02
#define ACK 0x01

using namespace std;

struct myPacket {
	
	uint16_t seq_num;			// Sequence number
	uint16_t num_packets;		// Total number of packets to be sent
	uint8_t window_size;		// Size of the sliding window
	uint8_t flags;				// Flag register
	uint16_t pl_size;			// Size of the payload
	uint8_t payload[1024];		// Payload, max size is 1024 bytes
	uint8_t checksum[16];		// It's the checksum
};

struct packet_entry {
    myPacket packet;
    timeval timestamp;
    int wasSent;
    int resends;
};

int numberOfPackets(char * filename) {

    //Get the filesize
    struct stat st;
    stat(filename, &st);
    int filesize = st.st_size;
	
	if((filesize % 1024) == 0)
		return (filesize / 1024);
		
	else 
		return ((filesize / 1024) + 1);
}

void print_md5_sum(unsigned char* md) {
    int i;
    for(i=0; i < 16; i++) {
            printf("%02x",md[i]);
    }
}

void computeMD5(myPacket * packet, uint8_t * digest) {

	//Create a local copy of the packet.
	myPacket local = *packet;
	
	//Set the checksum field in the local copy to zeros.
	bzero(local.checksum, 16);
	
	//Now compute the MD5 hash of the whole packet and put it in digest.
	MD5((const unsigned char *)&local, MY_PACKET_SIZE, (unsigned char *)digest);
}

int checkMD5(myPacket * packet) {
	
	uint8_t packetMD5[16];
	uint8_t compMD5[16];
	
	//Get the checksum specified in the packet.
	memcpy(packetMD5, packet->checksum, 16);
	
	//Now compute the checksum of said packet.
	computeMD5(packet, compMD5);
	
	/*
	cout << "Packet's MD5 is ";
	print_md5_sum((unsigned char *)packet->checksum);
	cout << endl;
	cout << "Computed MD5 is ";
	print_md5_sum((unsigned char *)compMD5);
	cout << endl;
	*/
	
	//Compare the two. Return 1 if good, 0 if bad.
	if (memcmp(packetMD5, compMD5, 16) == 0)
		return 1;
	else
		cout << "This packet is all messed up." << endl;
		return 0;
}

//Simple function
int calculateWindowSize(int total_packets) {

    if (total_packets < MAX_WINDOW_SIZE) {
	
    } else {
	return MAX_WINDOW_SIZE;
    }
}

myPacket generatePacket(int fd, int sequence_num, int total_packets, uint8_t flags) {

    	myPacket send_packet;
	bzero(&send_packet, sizeof(send_packet));

	char buf[1024];

	packet_entry list_entry;

	lseek(fd, sequence_num * 1024, SEEK_SET);
	int bytes_read = read(fd, buf, 1024);
	if (bytes_read == -1) {
	    perror("OH god read failed.");
	    exit(1);
	}

	//Fill in the packet with relevant data.
	send_packet.flags = flags;
	send_packet.seq_num = sequence_num;
	send_packet.num_packets = total_packets;
	send_packet.pl_size = bytes_read;
	memcpy(send_packet.payload, buf, bytes_read);

	return send_packet;

}
